"""Cookie池管理模块"""
import json
import random
import logging
from typing import List, Dict, Optional
from datetime import datetime, timedelta
import aioredis
import asyncio
import aiohttp
from ..security.cookie_encryption import cookie_encryptor

logger = logging.getLogger(__name__)


class CookiePool:
    """Cookie池管理器
    
    管理微博Cookie池，支持Cookie轮换、验证、更新
    """
    
    def __init__(self, redis_url: str = "redis://localhost:6379"):
        """初始化Cookie池
        
        Args:
            redis_url: Redis连接URL
        """
        self.redis_url = redis_url
        self.redis_client = None
        self.cookie_key = "weibo:cookies"
        self.cookie_score_key = "weibo:cookie:scores"
        self.min_score = 50  # 最低可用分数
        self.check_interval = 300  # 检查间隔（秒）
        
    async def connect(self):
        """连接Redis"""
        if not self.redis_client:
            self.redis_client = await aioredis.from_url(
                self.redis_url,
                encoding="utf-8",
                decode_responses=True
            )
            
    async def close(self):
        """关闭连接"""
        if self.redis_client:
            await self.redis_client.close()
            self.redis_client = None
            
    async def add_cookie(self, cookie: str, score: int = 100):
        """添加Cookie到池中
        
        Args:
            cookie: Cookie字符串
            score: 初始分数（默认100）
        """
        await self.connect()
        
        # 加密Cookie
        try:
            encrypted_cookie = cookie_encryptor.encrypt_cookie(cookie)
        except Exception as e:
            logger.error(f"Cookie加密失败: {e}")
            return
        
        # 生成Cookie ID作为键（使用前32个字符的哈希）
        import hashlib
        cookie_id = hashlib.md5(cookie.encode()).hexdigest()
        
        # 存储加密的Cookie
        await self.redis_client.hset(self.cookie_key, cookie_id, json.dumps({
            'encrypted_cookie': encrypted_cookie,
            'score': score,
            'add_time': datetime.utcnow().isoformat(),
            'last_use': None,
            'use_count': 0,
            'fail_count': 0
        }))
        
        # 更新分数（使用cookie_id作为键）
        await self.redis_client.zadd(self.cookie_score_key, {cookie_id: score})
        
        logger.info(f"添加加密Cookie到池中，当前分数: {score}")
        
    async def get_cookie(self) -> Optional[str]:
        """获取一个可用的Cookie
        
        Returns:
            Cookie字符串，如果没有可用Cookie返回None
        """
        await self.connect()
        
        # 获取分数大于最低分的Cookie ID
        cookie_ids = await self.redis_client.zrangebyscore(
            self.cookie_score_key,
            self.min_score,
            '+inf',
            withscores=True
        )
        
        if not cookie_ids:
            logger.warning("Cookie池中没有可用的Cookie")
            return None
            
        # 随机选择一个Cookie ID（优先选择高分的）
        weights = [score for _, score in cookie_ids]
        cookie_id = random.choices(
            [c for c, _ in cookie_ids],
            weights=weights,
            k=1
        )[0]
        
        # 获取并解密Cookie
        cookie_data_str = await self.redis_client.hget(self.cookie_key, cookie_id)
        if not cookie_data_str:
            logger.error(f"Cookie数据不存在: {cookie_id}")
            return None
            
        try:
            cookie_data = json.loads(cookie_data_str)
            encrypted_cookie = cookie_data['encrypted_cookie']
            decrypted_cookie = cookie_encryptor.decrypt_cookie(encrypted_cookie)
            
            if not decrypted_cookie:
                logger.error(f"Cookie解密失败: {cookie_id}")
                await self.mark_cookie_failed(cookie_id, "解密失败")
                return None
                
            # 更新使用记录
            await self._update_cookie_usage(cookie_id)
            
            return decrypted_cookie
            
        except Exception as e:
            logger.error(f"获取Cookie失败: {e}")
            return None
        
        
    async def _update_cookie_usage(self, cookie_id: str):
        """更新Cookie使用记录
        
        Args:
            cookie_id: Cookie ID
        """
        cookie_data = await self.redis_client.hget(self.cookie_key, cookie_id)
        if cookie_data:
            data = json.loads(cookie_data)
            data['last_use'] = datetime.utcnow().isoformat()
            data['use_count'] += 1
            
            await self.redis_client.hset(
                self.cookie_key,
                cookie_id,
                json.dumps(data)
            )
            
    async def mark_cookie_success(self, cookie_id: str):
        """标记Cookie使用成功
        
        Args:
            cookie_id: Cookie ID
        """
        await self.connect()
        
        # 增加分数（最高100）
        current_score = await self.redis_client.zscore(self.cookie_score_key, cookie_id)
        if current_score is not None:
            new_score = min(100, current_score + 5)
            await self.redis_client.zadd(self.cookie_score_key, {cookie_id: new_score})
            logger.debug(f"Cookie使用成功，分数: {current_score} -> {new_score}")
            
    async def mark_cookie_failed(self, cookie_id: str, reason: str = None):
        """标记Cookie使用失败
        
        Args:
            cookie_id: Cookie ID
            reason: 失败原因
        """
        await self.connect()
        
        # 减少分数
        current_score = await self.redis_client.zscore(self.cookie_score_key, cookie_id)
        if current_score is not None:
            new_score = max(0, current_score - 10)
            await self.redis_client.zadd(self.cookie_score_key, {cookie_id: new_score})
            
            # 更新失败计数
            cookie_data = await self.redis_client.hget(self.cookie_key, cookie_id)
            if cookie_data:
                data = json.loads(cookie_data)
                data['fail_count'] += 1
                data['last_fail'] = datetime.utcnow().isoformat()
                data['fail_reason'] = reason
                
                await self.redis_client.hset(
                    self.cookie_key,
                    cookie_id,
                    json.dumps(data)
                )
                
            logger.warning(f"Cookie使用失败，分数: {current_score} -> {new_score}，原因: {reason}")
            
            # 如果分数太低，移除Cookie
            if new_score < 10:
                await self.remove_cookie(cookie_id)
                
    async def remove_cookie(self, cookie_id: str):
        """从池中移除Cookie
        
        Args:
            cookie_id: Cookie ID
        """
        await self.connect()
        
        await self.redis_client.hdel(self.cookie_key, cookie_id)
        await self.redis_client.zrem(self.cookie_score_key, cookie_id)
        
        logger.info(f"Cookie已从池中移除")
        
    async def get_pool_status(self) -> Dict:
        """获取Cookie池状态
        
        Returns:
            池状态信息
        """
        await self.connect()
        
        total = await self.redis_client.zcard(self.cookie_score_key)
        available = await self.redis_client.zcount(
            self.cookie_score_key,
            self.min_score,
            '+inf'
        )
        
        # 获取分数分布
        score_ranges = {
            'high': await self.redis_client.zcount(self.cookie_score_key, 80, 100),
            'medium': await self.redis_client.zcount(self.cookie_score_key, 50, 79),
            'low': await self.redis_client.zcount(self.cookie_score_key, 0, 49)
        }
        
        return {
            'total': total,
            'available': available,
            'unavailable': total - available,
            'score_distribution': score_ranges,
            'min_score': self.min_score
        }
        
    async def validate_cookies(self):
        """验证所有Cookie的有效性
        
        定期检查Cookie是否仍然有效
        """
        await self.connect()
        
        # 获取所有Cookie ID
        cookie_ids = await self.redis_client.hkeys(self.cookie_key)
        
        for cookie_id in cookie_ids:
            try:
                # 获取并解密Cookie
                cookie_data_str = await self.redis_client.hget(self.cookie_key, cookie_id)
                if not cookie_data_str:
                    continue
                    
                cookie_data = json.loads(cookie_data_str)
                encrypted_cookie = cookie_data['encrypted_cookie']
                decrypted_cookie = cookie_encryptor.decrypt_cookie(encrypted_cookie)
                
                if not decrypted_cookie:
                    await self.mark_cookie_failed(cookie_id, "解密失败")
                    continue
                
                # 实际验证Cookie
                is_valid = await self._check_cookie_validity(decrypted_cookie)
                
                if is_valid:
                    await self.mark_cookie_success(cookie_id)
                else:
                    await self.mark_cookie_failed(cookie_id, "验证失败")
                    
            except Exception as e:
                logger.error(f"验证Cookie {cookie_id} 失败: {e}")
                await self.mark_cookie_failed(cookie_id, f"验证异常: {str(e)}")
                
            await asyncio.sleep(1)  # 避免请求过快
            
        logger.info(f"Cookie验证完成，共验证 {len(cookie_ids)} 个")
        
    async def _check_cookie_validity(self, cookie: str) -> bool:
        """检查Cookie有效性 - 实现真实验证
        
        Args:
            cookie: Cookie字符串
            
        Returns:
            是否有效
        """
        try:
            # 使用微博个人信息API验证Cookie
            headers = {
                'Cookie': cookie,
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
                'Accept': 'application/json, text/plain, */*',
                'Referer': 'https://weibo.com/'
            }
            
            # 请求用户配置接口，这个接口需要登录
            async with aiohttp.ClientSession() as session:
                async with session.get(
                    'https://weibo.com/ajax/config', 
                    headers=headers,
                    timeout=aiohttp.ClientTimeout(total=10)
                ) as response:
                    if response.status == 200:
                        data = await response.json()
                        # 如果返回数据包含uid说明登录有效
                        return 'uid' in data.get('data', {})
                    else:
                        return False
                        
        except Exception as e:
            logger.debug(f"Cookie验证失败: {e}")
            return False
        

class CaptchaHandler:
    """验证码处理器
    
    预留验证码识别接口
    """
    
    def __init__(self, api_key: str = None):
        """初始化验证码处理器
        
        Args:
            api_key: 验证码识别服务API密钥
        """
        self.api_key = api_key
        self.service_url = "https://api.captcha-service.com/solve"  # 示例URL
        
    async def detect_captcha(self, html: str) -> bool:
        """检测是否需要验证码
        
        Args:
            html: 页面HTML
            
        Returns:
            是否需要验证码
        """
        captcha_indicators = [
            '验证码',
            'captcha',
            'verify',
            '请输入验证码',
            '安全验证'
        ]
        
        html_lower = html.lower()
        return any(indicator in html_lower for indicator in captcha_indicators)
        
    async def solve_captcha(self, image_url: str) -> Optional[str]:
        """识别验证码
        
        Args:
            image_url: 验证码图片URL
            
        Returns:
            识别结果，失败返回None
        """
        if not self.api_key:
            logger.error("验证码识别服务未配置")
            return None
            
        # TODO: 实现验证码识别服务调用
        # 这里是接口预留
        logger.info(f"尝试识别验证码: {image_url}")
        return None
        
    async def handle_slider_captcha(self, page_context: Dict) -> bool:
        """处理滑块验证码
        
        Args:
            page_context: 页面上下文信息
            
        Returns:
            是否成功通过验证
        """
        # TODO: 实现滑块验证码处理
        # 可能需要使用Selenium或Playwright
        logger.info("检测到滑块验证码，需要处理")
        return False